// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


/**
 * @fileoverview Provides an API around the Layer Manager for the Layer UX in
 * the background.
 *
 * @author jasonstredwick@google.com (Jason Stredwick)
 */


goog.provide('bite.layer.client.Background');

goog.require('bite.layer.Messages');
goog.require('shellExtension.LayerManager');
goog.require('shellExtension.LayerManagerInstance');



/**
 * Constructs an object that manages the layer UX.
 * @constructor
 * @export
 */
bite.layer.client.Background = function() {
  /**
   * A string representing the base url for relative references.
   * @type {string}
   * @private
   */
  this.baseUrl_ = '';

  /**
   * Whether or not consoles are initialized.
   * @type {boolean}
   * @private
   */
  this.ready_ = false;

  /**
   * Whether or not the current state of the layer is on or off.
   * @type {boolean}
   * @private
   */
  this.isVisible_ = false;
};
goog.addSingletonGetter(bite.layer.client.Background);


/**
 * Handles responding to the Content Script once the LayerManager finished
 * processing a request.
 * @param {function(!*): void} response The response callback to be called
 *     when the request has finished.
 * @param {string} id The id of the layer that was modified.
 * @param {boolean} success Whether or not the request was successfully
 *     processed.
 * @private
 */
bite.layer.client.Background.handleResetLayerComplete_ =
    function(response, id, success) {
  var data = undefined;
  if (success) {
    data = shellExtension.layerManager.getLayerDetails(id);
    if (data && data.length == 1) {
      data = data[0];
    } else {
      data = undefined;
    }
  }
  response && response(data);
};


/**
 * Handles responding to the Content Script once the LayerManager finished
 * processing a request.
 * @param {function(!*): void} response The response callback to be called
 *     when the request has finished.
 * @param {boolean} success Whether or not the request was successfully
 *     processed.
 * @private
 */
bite.layer.client.Background.handleResetManagerComplete_ =
    function(response, success) {
  var data = [];
  if (success) {
    data = shellExtension.layerManager.getLayerDetails();
  }
  response && response(data);
};


/**
 * Handles responding to the Content Script once the LayerManager finished
 * processing a request.
 * @param {function(!*): void} response The response callback to be called
 *     when the request has finished.
 * @param {boolean} success Whether or not the request was successfully
 *     processed.
 * @private
 */
bite.layer.client.Background.handleRestoreDefaultsComplete_ =
    function(response, success) {
  var data = [];
  if (success) {
    data = shellExtension.layerManager.getLayerDetails();
  }
  response && response(data);
};


/**
 * Handles responding to the Content Script once the LayerManager finished
 * processing a request.
 * @param {function(!*): void} response The response callback to be called
 *     when the request has finished.
 * @param {string} id The id of the layer that was modified.
 * @param {boolean} success Whether or not the request was successfully
 *     processed.
 * @private
 */
bite.layer.client.Background.handleSetOnDemandComplete_ =
    function(response, id, success) {
  var data = undefined;
  if (success) {
    var details = shellExtension.layerManager.getLayerDetails(id);
    if (details && details.length == 1) {
      data = details[0];
    }
  }
  response && response(data);
};


/**
 * Handles responding to the Content Script once the LayerManager finished
 * processing a request.
 * @param {function(!*): void} response The response callback to be called
 *     when the request has finished.
 * @param {string} id The id of the layer that was modified.
 * @param {boolean} success Whether or not the request was successfully
 *     processed.
 * @private
 */
bite.layer.client.Background.handleSetStateComplete_ =
    function(response, id, success) {
  var data = undefined;
  if (success) {
    data = shellExtension.layerManager.getLayerDetails(id);
    if (data && data.length == 1) {
      data = data[0];
    } else {
      data = undefined;
    }
  }
  response && response(data);
};


/**
 * Process the CLOSE action.
 * @private
 */
bite.layer.client.Background.prototype.close_ = function() {
  this.isVisible_ = false;
};


/**
 * Process the CLOSE_CONTENT action that also sends a message to a tab to tell
 * it to close.
 * @param {number=} opt_tabId An optional tab id.
 * @private
 */
bite.layer.client.Background.prototype.closeContent_ = function(opt_tabId) {
  this.isVisible_ = false;

  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.CLOSE;

  if (opt_tabId) {
    chrome.tabs.sendRequest(opt_tabId, data);
  } else {
    chrome.tabs.getSelected(null, function(tab) {
      chrome.tabs.sendRequest(tab.id, data);
    });
  }
};


/**
 * Process the CONTENT_READY action that signifies that the content scripts
 * for a new tab have finished initializing and is requesting what to do next.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.contentReady_ = function(response) {
  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.UPDATE;
  data[bite.layer.Messages.Request.VISIBLE] = this.isVisible_;
  data[bite.layer.Messages.Request.LAYERS_DETAILS] =
      shellExtension.layerManager.getLayerDetails();

  response && response(data);
};


/**
 * Initializes the Layer UX for content scripts.
 * @param {string=} opt_baseUrl The base url to use for relative references.
 * @param {function(boolean)=} opt_callback An optional callback to be called
 *     when the init has completed and whether or not it was successful.
 * @export
 */
bite.layer.client.Background.prototype.init =
    function(opt_baseUrl, opt_callback) {
  if (this.ready) {
    opt_callback && opt_callback(true);
    return;
  }

  this.baseUrl_ = opt_baseUrl || '';

  var callback = opt_callback || goog.nullFunction;
  shellExtension.initLayerManager(this.baseUrl_,
      goog.bind(this.initComplete_, this, callback));
};


/**
 * Once the LayerManager has completed its setup then finish initialization.
 * @param {function(boolean)} callback The callback to fire when init has
 *     completed.
 * @param {boolean} success Whether or not the LayerManager reset was
 *     successful.
 * @private
 */
bite.layer.client.Background.prototype.initComplete_ =
    function(callback, success) {
  if (!success) {
    this.baseUrl_ = '';
    callback(success);
    return;
  }

  // Set up layer UX message handler.
  chrome.extension.onRequest.addListener(goog.bind(this.onRequest_, this));

  chrome.tabs.onSelectionChanged.addListener(
      goog.bind(this.onTabChange_, this));

  this.ready_ = true;
  callback(success);
};


/**
 * Tells whether or not layers are ready to be used.
 * @return {boolean} Whether or not layers are ready.
 * @export
 */
bite.layer.client.Background.prototype.isReady = function() {
  return this.ready_;
};


/**
 * Handles messages for the layer subsystem and redirects as appropriate.
 * @param {!Object} request The data sent.
 * @param {MessageSender} sender An object containing information about the
 *     script context that sent the request.
 * @param {function(!*): void} response Optional function to call when the
 *     request completes; only call when appropriate.
 * @private
 */
bite.layer.client.Background.prototype.onRequest_ =
    function(request, sender, response) {
  if (!this.ready_) {
    return;
  }

  var owner = request[bite.layer.Messages.Request.OWNER];
  var action = request[bite.layer.Messages.Request.ACTION];
  if (owner != bite.layer.Messages.Commands.OWNER || !action) {
    return;
  }

  switch (action) {
    case bite.layer.Messages.Commands.CLOSE:
      this.close_();
      break;

    case bite.layer.Messages.Commands.CLOSE_CONTENT:
      this.closeContent_(request[bite.layer.Messages.Request.TAB_ID]);
      break;

    case bite.layer.Messages.Commands.CONTENT_READY:
      this.contentReady_(response);
      break;

    case bite.layer.Messages.Commands.OPEN:
      this.open_(request[bite.layer.Messages.Request.TAB_ID]);
      break;

    case bite.layer.Messages.Commands.RESET:
      this.resetLayer_(request[bite.layer.Messages.Data.ID], response);
      break;

    case bite.layer.Messages.Commands.RESET_MANAGER:
      this.resetManager_(response);
      break;

    case bite.layer.Messages.Commands.RESTORE_DEFAULTS:
      this.restoreDefaults_(response);
      break;

    case bite.layer.Messages.Commands.SET_ONDEMAND:
      this.setOnDemand_(request[bite.layer.Messages.Data.ID],
                        request[bite.layer.Messages.Data.VALUE],
                        response);
      break;

    case bite.layer.Messages.Commands.SET_STATE:
      this.setState_(request[bite.layer.Messages.Data.ID],
                     request[bite.layer.Messages.Data.VALUE],
                     response);
      break;

    default:
      console.log('WARNING (bite.layer.client.Background.onRequest): ' +
                  'Unknown action - ' + request['action']);
  }
};


/**
 * Handles the onSelectionChange tab event.
 * @param {number} tabId The id of the tab that was just selected.
 * @param {Object} selectInfo An object containing information about the
 *     selection.
 * @private
 */
bite.layer.client.Background.prototype.onTabChange_ =
    function(tabId, selectInfo) {
  if (this.isVisible_) {
    this.open_(tabId);
  } else {
    this.closeContent_(tabId);
  }
};


/**
 * Process the OPEN action that sends a message to the content script to tell
 * it to turn on the interface.
 * @param {number=} opt_tabId An optional tab id.
 * @private
 */
bite.layer.client.Background.prototype.open_ = function(opt_tabId) {
  this.isVisible_ = true;

  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.OPEN;
  data[bite.layer.Messages.Request.LAYERS_DETAILS] =
      shellExtension.layerManager.getLayerDetails();

  if (opt_tabId) {
    chrome.tabs.sendRequest(opt_tabId, data);
  } else {
    chrome.tabs.getSelected(null, function(tab) {
      chrome.tabs.sendRequest(tab.id, data);
    });
  }
};


/**
 * Process the RESET command that cause the LayerManager to reset a layer.
 * @param {string} id The id for a layer.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.resetLayer_ = function(id, response) {
  shellExtension.layerManager.resetLayer(
      id,
      goog.partial(
          bite.layer.client.Background.handleResetLayerComplete_,
          response, id));
};


/**
 * Process the RESET_MANAGER that causes the LayerManager to reset.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.resetManager_ = function(response) {
  shellExtension.layerManager.reset(
      goog.partial(
          bite.layer.client.Background.handleResetManagerComplete_,
          response));
};


/**
 * Process the RESTORE_DEFAULTS that cause the LayerManager to restore its
 * default layer list.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.restoreDefaults_ = function(response) {
  shellExtension.layerManager.restoreDefaults(
      goog.partial(
          bite.layer.client.Background.handleRestoreDefaultsComplete_,
          response));
};


/**
 * Process the SET_ONDEMAND action that causes the layer with the given id to
 * changes its ondemand/load_immediate setting.
 * @param {string} id The id for a layer.
 * @param {shellExtension.Layer.State} value The value to change the
 *     setting to.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.setOnDemand_ =
    function(id, value, response) {
  shellExtension.layerManager.changeLoadImmediate(id, value,
      goog.partial(
          bite.layer.client.Background.handleSetOnDemandComplete_,
          response, id));
};


/**
 * Process the SET_STATE action that causes the layer with the given id to
 * changes its on/off setting.
 * @param {string} id The id for a layer.
 * @param {shellExtension.Layer.State} value The value to change the
 *     setting to.
 * @param {function(!*): void} response A function to call when the request
 *     completes.
 * @private
 */
bite.layer.client.Background.prototype.setState_ =
    function(id, value, response) {
  shellExtension.layerManager.changeState(id, value,
      goog.partial(
          bite.layer.client.Background.handleSetStateComplete_,
          response, id));
};

